#include "Col.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "Container.h"
#include "Class.h"

#include "IterIter.h"

#include "Error.h"
#include "String.h"
#include "ET_stdio.h"

const char
	*cWarningActiveIterator= "there are active iterators",
	*cWarningNullArgument  = "argument is a null pointer";

//---- Container ---------------------------------------------------------------

NewAbstractMetaImpl(Container,Object, (T(size), T(nDeleted), T(iterCount)));

Container *Container::currentContainer;

Container::Container()
{
	size= nDeleted= iterCount= 0;
	iterHead.Init();
}

Container::~Container()
{
	if (HasIterators())
		DetachIterators();
}

void Container::InitNew()
{
	size= nDeleted= iterCount= 0;
	iterHead.Init();
}

int Container::Capacity()
{
	return size;
}

bool Container::assertclass(Class *cl)
{
	register Object *op;
	Iter next(this);
	bool error= FALSE;

	if (cl == 0) {
		Error("assertclass", "class == 0");
		return TRUE;
	}
	for (int i= 0; op= next(); i++)
		if (! op->IsA()->isKindOf(cl)) {
			Error("assertclass", "element %d is not instance of class %s (%s)", i,
												cl->Name(), op->ClassName());
			error= TRUE;
		}
	return error;
}

void Container::InspectorId(char *b, int sz)
{
	static char cb[100];
	Object *op= AnyElement();

	if (op)
		sprintf(cb, "[%d] <%s>", Size(), op->ClassName());
	else
		sprintf(cb, "[%d]", Size());
	strn0cpy(b, cb, sz-1);
}

//---- comparing

bool Container::IsEqual(Object *col)
{
	if (IsA() != col->IsA())
		return FALSE;

	Iter next1(this), next2((Container*)col);
	register Object *op1, *op2;

	for (;;) {
		op1= next1();
		op2= next2();
		if (op1 == 0 && op2 == 0)
			return TRUE;
		if (op1 == 0 || op2 == 0)
			break;
		if (! op1->IsEqual(op2))
			break;
	}
	return FALSE;
}

u_long Container::Hash()
{
	Iter next(this);
	register Object *op;
	register unsigned long s= 0;

	while (op= next())
		s^= op->Hash();
	return s;
}

//---- accessing

Iterator *Container::MakeIterator(bool, void*)
{
	AbstractMethod("MakeIterator");
	return 0;
}

Object *Container::AnyElement()
{
	Iter next(this);
	return next();
}

Object *Container::Find(Object *anOp)
{
	register Object *op;

	if (anOp == 0)
		return 0;
	Iter next(this);
	while (op= next())
		if (op->IsEqual(anOp))
			return op;
	return 0;
}

Object *Container::FindPtr(Object *anOp)
{
	register Object *op;

	if (anOp == 0)
		return 0;
	Iter next(this);
	while (op= next())
		if (op == anOp)
			return op;
	return 0;
}

int Container::OccurrencesOf(Object *anOp)
{
	register Object *op;
	register int n= 0;

	if (anOp == 0) {
		Warning("OccurrencesOf", cWarningNullArgument);
		return 0;
	}
	Iter next(this);
	while (op= next())
		if (op->IsEqual(anOp))
			n++;
	return n;
}

int Container::OccurrencesOfPtr(Object *anOp)
{
	register Object *op;
	register int n= 0;

	if (anOp == 0)
		return FALSE;
	Iter next(this);
	while (op= next())
		if (op == anOp)
			n++;
	return n;
}

//---- object i/o

OStream &Container::PrintOn(OStream &os)
{
	Object::PrintOn(os);
	os << size SP;
	return os;
}

IStream &Container::ReadFrom(IStream &is)
{
	Object::ReadFrom(is);
	is >> size;
	iterHead.Init();
	return is;
}

//---- robust iterators

void Container::IteratorActivates(Iterator *it)
{
	if (it) {
		iterCount++;
		it->ChainIn(&iterHead);
	} else
		Warning("IteratorActivates", cWarningNullArgument);
}

void Container::IteratorTerminates(Iterator *it)
{
	if (it) {
		iterCount--;
		it->ChainOut();
		if (iterCount < 0)
			Error("IteratorTerminate", "iterCount < 0");
		if (HasDeletedObjects() && iterCount <= 0)
			RemoveDeleted();
	} else
		Warning("IteratorTerminates", cWarningNullArgument);
}

void Container::DetachIterators()
{
	register Iterator *it, *jt;

	it= iterHead.next;
	while (it != &iterHead) {
		jt= it;
		it= it->next;
		jt->Detach();
		jt->ChainOut();
	}
}

//---- robust iterators - Deleted Object approach (old style)

void Container::RemoveDeleted()
{
	// hook
}

//---- Iter --------------------------------------------------------------------

Iter::~Iter()
{
	if (seq) {
		if ((char*)seq == space)
			seq->~Iterator();
		else
			delete seq;
		seq= 0;
	}
}

void Iter::Reset(Container *coll, bool dir)
{
	if (seq) {
		if ((char*)seq == space)
			delete seq;
		else
			seq->~Iterator();
		seq= 0;
	}
	seq= coll->MakeIterator(dir, space);
}

